home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Atari Compendium
/
The Atari Compendium (Toad Computers) (1994).iso
/
files
/
umich
/
network
/
ka9q
/
nhclb120.zoo
/
iproute.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-06-18
|
16KB
|
643 lines
/* Lower half of IP, consisting of gateway routines
* Includes routing and options processing code
*/
#include "global.h"
#include "mbuf.h"
#include "internet.h"
#include "timer.h"
#include "netuser.h"
#include "ip.h"
#include "icmp.h"
#include "iface.h"
#include "trace.h"
#ifdef UNIX
#include <memory.h>
#endif
static int16 hash_ip();
static int32 get32();
static struct route *rt_lookup();
struct route *routes[32][NROUTE]; /* Routing table */
struct route r_default; /* Default route entry */
int32 ip_addr;
struct ip_stats ip_stats;
#ifndef GWONLY
struct mbuf *loopq; /* Queue for loopback packets */
#endif
/* Route an IP datagram. This is the "hopper" through which all IP datagrams,
* coming or going, must pass.
*
* "rxbroadcast" is set to indicate that the packet came in on a subnet
* broadcast. The router will kick the packet upstairs regardless of the
* IP destination address.
*/
int
ip_route(bp,rxbroadcast)
struct mbuf *bp;
char rxbroadcast; /* True if packet had link broadcast address */
{
struct mbuf *htonip();
void ip_recv();
struct ip ip; /* IP header being processed */
int16 ip_len; /* IP header length */
int16 length; /* Length of data portion */
int32 gateway; /* Gateway IP address */
register struct route *rp; /* Route table entry */
struct interface *iface; /* Output interface, possibly forwarded */
struct route *rt_lookup();
int16 offset; /* Offset into current fragment */
int16 mf_flag; /* Original datagram MF flag */
int strict = 0; /* Strict source routing flag */
char precedence; /* Extracted from tos field */
char delay;
char throughput;
char reliability;
int16 opt_len; /* Length of current option */
char *opt; /* -> beginning of current option */
char *ptr; /* -> pointer field in source route fields */
struct mbuf *tbp;
ip_stats.total++;
if(len_mbuf(bp) < IPLEN){
/* The packet is shorter than a legal IP header */
ip_stats.runt++;
free_p(bp);
return -1;
}
/* Sneak a peek at the IP header's IHL field to find its length */
ip_len = (bp->data[0] & 0xf) << 2;
if(ip_len < IPLEN){
/* The IP header length field is too small */
ip_stats.length++;
free_p(bp);
return -1;
}
if(cksum(NULLHEADER,bp,ip_len) != 0){
/* Bad IP header checksum; discard */
ip_stats.checksum++;
free_p(bp);
return -1;
}
/* Extract IP header */
ntohip(&ip,&bp);
if(ip.version != IPVERSION){
/* We can't handle this version of IP */
ip_stats.version++;
free_p(bp);
return -1;
}
/* Trim data segment if necessary. */
length = ip.length - ip_len; /* Length of data portion */
trim_mbuf(&bp,length);
/* Process options, if any. Also compute length of secondary IP
* header in case fragmentation is needed later
*/
strict = 0;
for(opt = ip.options; opt < &ip.options[ip.optlen];opt += opt_len){
int32 get32();
/* Most options have a length field. If this is a EOL or NOOP,
* this (garbage) value won't be used
*/
opt_len = uchar(opt[1]);
switch(opt[0] & OPT_NUMBER){
case IP_EOL:
goto no_opt; /* End of options list, we're done */
case IP_NOOP:
opt_len = 1;
break; /* No operation, skip to next option */
case IP_SSROUTE: /* Strict source route & record route */
strict = 1; /* note fall-thru */
case IP_LSROUTE: /* Loose source route & record route */
/* Source routes are ignored unless we're in the
* destination field
*/
if(ip.dest != ip_addr)
break; /* Skip to next option */
if(uchar(opt[2]) >= opt_len){
break; /* Route exhausted; it's for us */
}
/* Put address for next hop into destination field,
* put our address into the route field, and bump
* the pointer
*/
ptr = opt + uchar(opt[2]) - 1;
ip.dest = get32(ptr);
put32(ptr,ip_addr);
opt[2] += 4;
break;
case IP_RROUTE: /* Record route */
if(uchar(opt[2]) >= opt_len){
/* Route area exhausted; kick back an error */
union icmp_args icmp_args;
icmp_args.pointer = IPLEN + opt - ip.options;
icmp_output(&ip,bp,PARAM_PROB,0,&icmp_args);
free_p(bp);
return -1;
}
/* Add our address to the route */
ptr = opt + uchar(opt[2]) - 1;
ptr = put32(ptr,ip_addr);
opt[2] += 4;
break;
}
}
no_opt:
/* See if it's a broadcast or addressed to us, and kick it upstairs */
if(ip.dest == ip_addr || (ip_addr == 0 && ip.source) || rxbroadcast){
#ifdef GWONLY
/* We're only a gateway, we have no host level protocols */
if(!rxbroadcast)
icmp_output(&ip,bp,DEST_UNREACH,PROT_UNREACH,(union icmp_args *)NULL);
free_p(bp);
#else
/* If this is a local loopback packet, place on the loopback
* queue for processing in the main loop. This prevents the
* infinite stack recursion and other problems that would
* otherwise occur when we talk to ourselves, e.g., with ftp
*/
if(ip.source == ip_addr){
/* Put IP header back on */
if((tbp = htonip(&ip,bp,0)) == NULLBUF){
free_p(bp);
return -1;
}
/* Copy loopback packet into new buffer.
* This avoids an obscure problem with TCP which
* dups its outgoing data before transmission and
* then frees it when an ack comes, even though the
* receiver might not have actually read it yet
*/
bp = copy_p(tbp,len_mbuf(tbp));
free_p(tbp);
if(bp == NULLBUF)
return -1;
enqueue(&loopq,bp);
} else {
ip_recv(&ip,bp,rxbroadcast);
}
#endif
return 0;
}
/* Decrement TTL and discard if zero */
if(--ip.ttl == 0){
/* Send ICMP "Time Exceeded" message */
icmp_output(&ip,bp,TIME_EXCEED,0,NULLICMP);
free_p(bp);
return -1;
}
/* Look up target address in routing table */
if((rp = rt_lookup(ip.dest)) == NULLROUTE){
/* No route exists, return unreachable message */
icmp_output(&ip,bp,DEST_UNREACH,HOST_UNREACH,NULLICMP);
free_p(bp);
return -1;
}
/* Check for output forwarding and divert if necessary */
iface = rp->interface;
if(iface->forw != NULLIF)
iface = iface->forw;
/* Find gateway; zero gateway in routing table means "send direct" */
if(rp->gateway == (int32)0)
gateway = ip.dest;
else
gateway = rp->gateway;
if(strict && gateway != ip.dest){
/* Strict source routing requires a direct entry */
icmp_output(&ip,bp,DEST_UNREACH,ROUTE_FAIL,NULLICMP);
free_p(bp);
return -1;
}
precedence = PREC(ip.tos);
delay = ip.tos & DELAY;
throughput = ip.tos & THRUPUT;
reliability = ip.tos & RELIABILITY;
if(ip.length <= iface->mtu){
/* Datagram smaller than interface MTU; put header
* back on and send normally
*/
if((tbp = htonip(&ip,bp,0)) == NULLBUF){
free_p(bp);
return -1;
}
return (*iface->send)(tbp,iface,gateway,
precedence,delay,throughput,reliability);
}
/* Fragmentation needed */
if(ip.fl_offs & DF){
/* Don't Fragment set; return ICMP message and drop */
icmp_output(&ip,bp,DEST_UNREACH,FRAG_NEEDED,NULLICMP);
free_p(bp);
return -1;
}
/* Create fragments */
offset = (ip.fl_offs & F_OFFSET) << 3;
mf_flag = ip.fl_offs & MF; /* Save original MF flag */
while(length != 0){ /* As long as there's data left */
int16 fragsize; /* Size of this fragment's data */
struct mbuf *f_data; /* Data portion of fragment */
/* After the first fragment, should remove those
* options that aren't supposed to be copied on fragmentation
*/
ip.fl_offs = offset >> 3;
if(length + ip_len <= iface->mtu){
/* Last fragment; send all that remains */
fragsize = length;
ip.fl_offs |= mf_flag; /* Pass original MF flag */
} else {
/* More to come, so send multiple of 8 bytes */
fragsize = (iface->mtu - ip_len) & 0xfff8;
ip.fl_offs |= MF;
}
ip.length = fragsize + ip_len;
/* Move the data fragment into a new, separate mbuf */
if((f_data = alloc_mbuf(fragsize)) == NULLBUF){
free_p(bp);
return -1;
}
f_data->cnt = pullup(&bp,f_data->data,fragsize);
/* Put IP header back on */
if((tbp = htonip(&ip,f_data,0)) == NULLBUF){
free_p(f_data);
free_p(bp);
return -1;
}
/* and ship it out */
if((*iface->send)(tbp,iface,gateway,
precedence,delay,throughput,reliability) == -1)
return -1;
offset += fragsize;
length -= fragsize;
}
return 0;
}
struct rt_cache rt_cache;
/* Add an entry to the IP rou